/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.isfd.util.bdfparser.parsers;

import cn.hutool.core.collection.CollectionUtil;
import com.comac.ins.common.core.constant.CommonConstant;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.isfd.util.bdfparser.entities.bdf.rawdata.ParseFileOptions;
import com.comac.ins.isfd.util.bdfparser.entities.bdf.rawdata.RawDataLine;
import com.comac.ins.isfd.util.bdfparser.enums.BeginFlagEnum;
import com.comac.ins.isfd.util.bdfparser.enums.DeckTypeEnum;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * BDF文件读取
 */
@Slf4j
public class BDFFileUtils {

    /**
     * 读取 BDF 文件
     *
     * @param bdfFilePath BDF 文件路径
     * @param options     解析选项
     * @return 解析后的数据列表
     * @throws IOException 文件读取或解析失败
     */
    public static Map<String, List<RawDataLine>> readBDFFile(Path bdfFilePath,
                                                             ParseFileOptions options) {
        boolean readIncludes = options.isReadIncludes();
        boolean punch = options.isPunch();
        Optional<String> encoding = options.getEncoding();
        Path basePath = bdfFilePath.getParent().toAbsolutePath();

        log.info("读取BDF主文件： {}", bdfFilePath);
        return readFileWithIncludes(basePath, bdfFilePath, readIncludes, encoding, punch);
    }

    /**
     * 递归读取include文件
     */
    private static Map<String, List<RawDataLine>> readFileWithIncludes(Path basePath, Path bdfFilePath, boolean readIncludes, Optional<String> encoding, boolean punch) {
        log.info("读取BDF文件： {}", bdfFilePath);
        List<RawDataLine> rawDataLines = readFile(basePath, bdfFilePath, encoding);
        Map<String, List<RawDataLine>> rawDataLineSectioned = convertRawDataLinesToBDFLines(rawDataLines, punch); // 原始数据分区

        if (readIncludes) {
            List<String> includeLines = parseIncludeLines(rawDataLines);
            if (CollectionUtil.isEmpty(includeLines)) {
                return rawDataLineSectioned;
            }
            log.info("读取到INCLUDE 信息");

            // 记录已处理的文件路径，防止重复读取
            Set<Path> visitedFiles = new HashSet<>();
            visitedFiles.add(bdfFilePath);
            List<Path> includeFilePaths = getIncludeFilePaths(includeLines, basePath);
            // 过滤未访问过的文件路径
            List<Path> unvisitedFilePaths = includeFilePaths.stream()
                .filter(path -> !visitedFiles.contains(path)) // 排除已处理的文件
                .peek(visitedFiles::add) // 标记为已处理
                .collect(Collectors.toList());

            for (Path path : unvisitedFilePaths) {
                try {
                    Map<String, List<RawDataLine>> newRawDataLineSectioned = readFileWithIncludes(basePath, path, true, encoding, punch);
                    newRawDataLineSectioned.forEach((key, newList) -> {
                        rawDataLineSectioned.merge(key, newList,
                            (existingList, additionalList) -> {
                                existingList.addAll(additionalList);
                                return existingList;
                            });
                    });
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
        return rawDataLineSectioned;
    }


    private static List<RawDataLine> readFile(Path basePath, Path bdfFilePath, Optional<String> encoding) {
        // 验证文件路径
        if (bdfFilePath == null || !bdfFilePath.toFile().exists()) {
            log.error("文件路径无效或文件不存在：" + bdfFilePath);
            throw new IllegalArgumentException("文件路径无效或文件不存在：" + bdfFilePath);
        }
        // 计算相对路径
        Path relativePath = basePath.relativize(bdfFilePath);

        // 使用 UTF-8 作为默认编码
        String fileEncoding = encoding.orElse("UTF-8");
        try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(Files.newInputStream(bdfFilePath), fileEncoding))) {
            List<RawDataLine> rawDataLines = new ArrayList<>();
            int rowNumber = 0;
            String line;
            while ((line = reader.readLine()) != null) {
                rawDataLines.add(new RawDataLine(line, relativePath.toString(), ++rowNumber));
            }
            return rawDataLines;
        } catch (IOException e) {
            log.error("文件读取错误：" + e.getMessage());
            throw new BaseException("文件读取错误：" + e.getMessage());
        }
    }

    /**
     * 获取include文本行
     */
    private static List<RawDataLine> getIncludeLines(List<RawDataLine> rawDataLines) {
        List<RawDataLine> includeLines = new ArrayList<>();
        List<RawDataLine> currentInclude = null;

        Iterator<RawDataLine> iterator = rawDataLines.iterator();
        while (iterator.hasNext()) {
            RawDataLine rawDataLine = iterator.next();

            String line = rawDataLine.getLine();
            // 去掉注释部分，仅保留代码部分
            String[] splitLines = line.split("\\$");
            line = splitLines.length > 0 ? splitLines[0] : "";

            if (StringUtils.isBlank(line)) {
                continue; // 跳过空行
            }

            if (line.toUpperCase().startsWith("INCLUDE ")) {
                if (currentInclude != null) {
                    includeLines.addAll(currentInclude); // 保存之前的 INCLUDE 块
                }
                currentInclude = new ArrayList<>();
            }

            if (currentInclude != null) {
                currentInclude.add(rawDataLine);
                iterator.remove();

                if (line.endsWith("'")) {
                    includeLines.addAll(currentInclude); // INCLUDE 块结束
                    currentInclude = null;
                }
            }
        }

        return includeLines;
    }


    /**
     * 解析include文本行
     */
    public static List<String> parseIncludeLines(List<RawDataLine> rawDataLines) {
        List<String> includeLines = new ArrayList<>();
        StringBuilder currentInclude = new StringBuilder();
        boolean inInclude = false;
        for (RawDataLine rawDataLine : rawDataLines) {
            String line = rawDataLine.getLine();
            // 去掉注释部分，仅保留代码部分
            String[] splitLines = line.split("\\$");
            line = splitLines.length > 0 ? splitLines[0] : "";
            if (StringUtils.isBlank(line)) {
                continue; // 跳过空行或长度不足的行
            }
            if (line.toUpperCase().startsWith("INCLUDE ")) {
                if (inInclude && currentInclude.length() > 0) {
                    includeLines.add(removeQuotes(currentInclude.toString()));
                    currentInclude = new StringBuilder();
                }
                inInclude = true;
                currentInclude.append(line.substring(8).trim());
            } else if (inInclude) {
                currentInclude.append(line.trim());
                if (line.endsWith("'")) {
                    // 当前 INCLUDE 完成
                    includeLines.add(removeQuotes(currentInclude.toString()));
                    currentInclude.setLength(0);
                    inInclude = false;
                }
            }
        }
        // 处理最后一个 INCLUDE 块
        if (inInclude && currentInclude.length() > 0) {
            // 如果路径没有正确闭合，则不添加
            if (!currentInclude.toString().endsWith("'")) {
                return includeLines;  // 不添加不完整的路径
            }
            // 去掉首尾的单引号
            includeLines.add(removeQuotes(currentInclude.toString()));
        }
        return includeLines;
    }

    /**
     * 获取文件名
     * 如：INCLUDE 'C919S_S10_R2_D20240124_section_node.bdf'
     * ---> C919S_S10_R2_D20240124_section_node
     */
    public static String getIndexFileNameWithLine(String line) {
        String fileName = line;
        if (line.startsWith(CommonConstant.INCLUDE)) {
            //1. 每一行先截取掉 INCLUDE
            fileName = line.substring(CommonConstant.INCLUDE.length());
            //2. 继续截取掉前后的' 单引号
            if (fileName.startsWith("'") && fileName.endsWith("'")) {
                fileName = fileName.substring(1, fileName.length() - 1).trim();
            }
            //补一刀
            if (fileName.contains("'")) {
                fileName = fileName.replaceAll("'", "");
            }
            //再补一刀
            if (StringUtils.isNotBlank(fileName)) {
                fileName = fileName.trim();
            }
            if (fileName.contains(".")) {
                fileName = StringUtils.substringBeforeLast(fileName, ".");
            }
        }
        return fileName;
    }

    // 去掉字符串的首尾单引号
    private static String removeQuotes(String str) {
        if (str.startsWith("'") && str.endsWith("'")) {
            return str.substring(1, str.length() - 1); // 去掉首尾的单引号
        }
        return str; // 如果没有单引号，则返回原字符串
    }

    /**
     * 获取完整include文件路径
     * INCLUDE 'Satellite_V02_INCLUDE:Satellite_V02_Panneau_Externe.dat'
     * INCLUDE '../../BULK/COORDS/satellite_V02_Coord.blk'
     */
    private static List<Path> getIncludeFilePaths(List<String> includeLines, Path basePath) {
        List<Path> includeFilePaths = new ArrayList<>();
        for (String includeLine : includeLines) {
            // 去掉引号并修剪两端的空白字符
            String pathStr = includeLine.trim().replaceAll("^'|'$", "");
            // 如果路径是相对路径，则拼接基础路径
            Path filePath;
            if (pathStr.startsWith("/")) {
                // 如果路径是以'/'开始，那么它是绝对路径
                filePath = Paths.get(pathStr).toAbsolutePath();
            } else {
                // 否则拼接基础路径
                filePath = basePath.resolve(pathStr).normalize(); // 使用 normalize() 来处理 '../' 和 './'
            }
            includeFilePaths.add(filePath);
        }
        return includeFilePaths;
    }

    /**
     * 将原始数据lines分割为BDF格式的lines
     * 1. NASTRAN statement, 2. file management statement, 3. executive control statement
     * 4. case control command, 5. bulk data entries
     * todo: 实现顺序：BULK_DATA_ENTRIES, FILE_MANAGEMENT_STATEMENT, NASTRAN_STATEMENT...
     */
    private static Map<String, List<RawDataLine>> convertRawDataLinesToBDFLines(List<RawDataLine> rawDataLines, boolean punch) {
        Map<String, List<RawDataLine>> decks = new HashMap<>();
        List<RawDataLine> systemLines = new ArrayList<>();
        List<RawDataLine> fileManagementLines = new ArrayList<>();
        List<RawDataLine> executiveControlLines = new ArrayList<>();
        List<RawDataLine> caseControlLines = new ArrayList<>();
        List<RawDataLine> bulkDataLines = new ArrayList<>(rawDataLines);

        // 获取文件管理行
        List<RawDataLine> includeLines = getIncludeLines(bulkDataLines);
        fileManagementLines.addAll(includeLines);

        if (punch) {
            decks.put(DeckTypeEnum.FILE_MANAGEMENT_STATEMENT.getType(), fileManagementLines);
            decks.put(DeckTypeEnum.BULK_DATA_ENTRY.getType(), bulkDataLines);

            return decks;
        }
        List<RawDataLine> activeLines = systemLines;
        for (RawDataLine rawDataLine : rawDataLines) {
            // 获取行字符串内容
            String line = rawDataLine.getLine();

            // 去掉注释部分，仅保留代码部分
            String[] splitLines = line.split("\\$");
            line = splitLines.length > 0 ? splitLines[0] : "";
            if (StringUtils.isBlank(line)) {
                continue; // 跳过空行或长度不足的行
            }
            if (line.toUpperCase().startsWith("BEGIN")) {
                if (getBeginFlag(line).equals(BeginFlagEnum.BULK.getType())) {
                    activeLines = bulkDataLines;
                }
            } else {
                activeLines.add(rawDataLine);
            }
        }

        decks.put(DeckTypeEnum.BULK_DATA_ENTRY.getType(), bulkDataLines);
        return decks;
    }

    private static String getBeginFlag(String line) {
        String lineUpper = line.toUpperCase();
        // 判断是否为 'BEGIN', 'BEGINBULK' 或 'BEGIN BULKS'
        if (lineUpper.equals("BEGIN") || lineUpper.equals("BEGINBULK") || lineUpper.equals("BEGIN BULKS")) {
            lineUpper = "BEGIN BULK"; // 统一设置为 'BEGIN BULK'
        }
        if (lineUpper.equals("BEGIN BULK")) {
            return BeginFlagEnum.BULK.getType();
        }
        return lineUpper;
    }


    // 测试
    public static void main(String[] args) {
        String filename = "C:\\Users\\lijpt\\OneDrive\\Documents\\COMAC\\ISFD\\model_parsing\\bdf_file\\解析测试\\测试任务2\\C919\\1866679839427321858.bdf";
        filename = "data\\mesh\\C919\\克卜勒-R2\\1741335387169\\model\\C919S_S11_R2_D20240222_NLG.bdf";
        Path bdfFilePath = Paths.get(filename).toAbsolutePath();
        try {
            ParseFileOptions options = new ParseFileOptions();
            Map<String, List<RawDataLine>> metaData = readBDFFile(bdfFilePath, options);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}
